学习说明:本章只计“平台折扣方法+中国企业财务约束代理”成果;企业季度财报没有订单级折扣、成交价和商品日暴露,不计真实电商订单完成案例。
电商平台的核心数据分析问题:
| 要素 | 课堂口径 |
|---|---|
| 本地资产 | stock/financial_statement.h5、stock/stock_basic_data.h5 |
| 对象与期间 | 南京焦点科技 002315.SZ,2018–2025 年季度数据 |
| 字段 | 地区/行业、营业收入、销售费用、净利润 |
| 决策 | 折扣方案不得突破销售费用率与净利率双重阈值 |
平台订单样例用于学习折扣—利润分析;真实公司财报只检查预算约束。缺少订单级成交价与优惠字段时,不得声称识别了因果性的“最优折扣”。
数据来源:某国际电商平台(Superstore)销售数据
分析框架:
pd.read_excel() 读取 Excel 格式销售数据Postal Code 字段.shape 和 .head() 初步了解数据规模与结构分析方法:按订单日期分组,统计每天折扣种类数量
分析要求:先计算 daily_discounts.gt(1) 的实际占比、涉及日期数和缺失情况,再说明同一天出现多种折扣的现象。订单数据由教学平台用于固定练习,公开教材的拓展案例使用课程数据下载入口提供的企业财务数据。
分析方法:按子品类和折扣力度分组,统计利润与天数
# 注:依赖 superstore.xlsx 数据,此处展示分析逻辑
# category_discount = data.groupby(['Sub-Category', 'Discount']).agg({
# 'Profit': 'sum', # 计算该品类在该折扣下的总利润
# 'Order Date': 'nunique' # 统计该折扣持续的天数
# })
# category_discount['Profit_per_day'] = (
# category_discount['Profit'] / category_discount['Order Date']
# )
# print(category_discount.head(15))待执行判据:只有各品类折扣分布、样本量和期间均由实际输出支持后,才能描述品类差异;当前只保留待检验假设。
核心思路:剔除品类数量和折扣时间的影响
# 注:依赖 superstore.xlsx 数据,此处展示分析逻辑
# profit_by_discount = data.groupby('Discount').agg({
# 'Quantity': 'sum', # 总销量
# 'Sales': 'sum', # 总销售额
# 'Profit': 'sum', # 总利润
# 'Order Date': 'nunique', # 不重复日期数
# 'Product Name': 'nunique' # 不重复产品数
# })
# profit_by_discount.columns = [
# 'Quantity_sum', 'Sales_sum', 'Profit_sum',
# 'Dates_sum', 'Products_sum'
# ]核心公式:
\[ \large \text{观测产品日平均利润} = \frac{\sum_{c\in C_d}\text{Profit}_c}{|C_d|} \]
其中 \(C_d\) 是折扣 \(d\) 下实际出现的“产品×订单日”单元集合。不能用“产品去重数×日期去重数”补出未发生的笛卡尔网格;若研究完整暴露网格,必须另有在售/可售记录并明确零成交单元。
# 注:依赖 superstore.xlsx 数据,此处展示实际观测单元逻辑
# observed_cells = data.groupby(['Discount','Product Name','Order Date'], as_index=False).agg(
# Profit=('Profit','sum'), Sales=('Sales','sum'), Quantity=('Quantity','sum')
# ) # 先把同一折扣、产品、订单日的多行合并为一个实际观测单元
# profit_by_discount = observed_cells.groupby('Discount').agg(
# Profit_p_p=('Profit','mean'), Sales_p_p=('Sales','mean'),
# Quantity_p_p=('Quantity','mean'), Observed_cells=('Profit','size')
# ) # 仅在实际出现的产品日单元上计算均值
# assert observed_cells['Profit'].sum() == data['Profit'].sum() # 核对利润守恒# 注:依赖 superstore.xlsx 数据,此处展示分析逻辑
# profit_filtered = profit_by_discount.query('Products_sum > 19')
# sns.set(style='darkgrid')
# plt.figure(figsize=(10, 6))
# for metric, label in zip(
# ['Profit_p_p', 'Sales_p_p', 'Quantity_p_p'],
# ['平均利润', '平均销售额', '平均销售数量']
# ):
# sns.lineplot(x='Discount', y=metric, label=label, data=profit_filtered)
# plt.legend()
# plt.xlabel('折扣')
# plt.ylabel('数值')
# plt.title('折扣与业务指标关系')
# plt.show()“折扣 20% 是利润正负拐点”仅是平台材料中的待检验假设;本仓库没有可访问订单快照,不发布事实结论。
| 折扣区间 | 利润情况 | 策略建议 |
|---|---|---|
| < 20% | 待运行检验 | 不预称安全区间 |
| > 20% | 待运行检验 | 不预称负利润区间 |
低折扣策略只是待检验候选
85%、55%、57% 是否出现异常负利润必须由可访问数据核对;当前只列为待检验异常点。
| 策略方向 | 具体建议 |
|---|---|
| 常规定价 | 先按折扣组报告样本量、利润与不确定性,再审批阈值 |
| 促销策略 | 将 0.2%、7%、20% 作为候选敏感性点,不预称最优 |
| 亏损核对 | 先确认实际观测、成本和不确定性,再决定是否继续小范围试用 |
| 品类比较 | 先比较各品类样本量与折扣分布;依据不足时只作为待验证的假设 |
data、huigui01、huigui03、profit_8discount 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。ax 的结果;同时写出方向、数量级或表格/图形结构。# 注:superstore.xlsx 数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
# 导入必要的包
import pandas as pd # 用于数据处理和分析
import matplotlib.pyplot as plt # 用于绘图
import seaborn as sns # 用于高级绘图
from datetime import datetime # 用于处理日期时间数据
# 读取Excel文件
data = pd.read_excel('superstore.xlsx')
# 删除Postal Code字段
del data['Postal Code']
print(data.head()) # 输出前几行数据
# 折扣与销售利润关系探究
# 判断每一天是不是同一个折扣
huigui01 = data.groupby('Order Date').Discount.nunique()
print(huigui01.head()) # 输出前几行数据
# 判断每个品类的折扣情况
huigui03 = data.groupby(['Sub-Category', 'Discount']).agg({
'Profit': 'sum', # 计算每个子类别和折扣组合的总利润
'Order Date': 'nunique', # 计算每个子类别和折扣组合的唯一订单日期数量
}) # 数据结构定义结束
huigui03['Profit_pd'] = huigui03['Profit'] / huigui03['Order Date'] # 计算单位时间内的平均利润
print(huigui03.head(15)) # 打印前15行结果,检查每个子类别和折扣组合的利润情况
# 通过前面的判断,可以发现:
# 1)同一天不同商品会采取不同的折扣;
# 2)不同品类的商品,折扣力度也并不相同;
# 为了简化问题,我们将所有销售数据按照折扣分组,然后对销量、销售额、利润等数据进行汇总聚合;
# 同时,考虑到折扣促销的本质,就是为了通过降价提高销量,相应单个产品的利润会有所下降,但是单个品类、单位时间的利润总和会得到提升;
# 因此我们在探究折扣与利润之间关系时候,需要剔除品类数量、折扣时间的影响。
# 按折扣分组,计算汇总指标
profit_8discount = data.groupby('Discount').agg({
'Quantity': 'sum', # 计算每个折扣下的总销售数量
'Sales': 'sum', # 计算每个折扣下的总销售额
'Profit': 'sum', # 计算每个折扣下的总利润
'Order Date': 'nunique', # 计算每个折扣下的唯一订单日期数量
'Product Name': 'nunique', # 计算每个折扣下的唯一商品名称数量
}) # 数据结构定义结束
# 重命名列
profit_8discount.columns = ['Quantity_sum', 'Sales_sum', 'Profit_sum', 'Dates_sum', 'Products_sum']
# 计算单位时间内每个商品的平均利润、平均销售额和平均销售数量
profit_8discount['Profit_p_p'] = profit_8discount['Profit_sum'] / profit_8discount['Products_sum'] / profit_8discount['Dates_sum'] # 平均利润
profit_8discount['Sales_p_p'] = profit_8discount['Sales_sum'] / profit_8discount['Products_sum'] / profit_8discount['Dates_sum'] # 平均销售额
profit_8discount['Quantity_p_p'] = profit_8discount['Quantity_sum'] / profit_8discount['Products_sum'] / profit_8discount['Dates_sum'] # 平均销售数量
# 重置索引,方便后续操作
profit_8discount = profit_8discount.reset_index()
print(profit_8discount) # 输出利润数据
# 选取折扣涉及商品种类大于(商品种类总数)*0.005的情形;
profit_8discount22 = profit_8discount.query('Products_sum > 19')
print(profit_8discount22) # 输出利润数据
# 绘制折扣与利润关系的折线图
sns.set(style="darkgrid") # 设置绘图风格为'darkgrid'
plt.figure(figsize=(10, 6)) # 设置画布大小
# 遍历需要绘制的列(平均利润、平均销售额、平均销售数量)
for i in profit_8discount22.columns[6:-2]:
ax = sns.lineplot(x='Discount', y=i, label=i, data=profit_8discount22) # 绘制折线图
# 添加图例和标签
plt.legend() # 显示图例
plt.xlabel('Discount Range') # 设置x轴标签
plt.ylabel('Sales') # 设置y轴标签
plt.savefig("10.png") # 保存图表为PNG文件
plt.show() # 显示图表
# 通过分析发现:
# 以下三点为平台固定材料中的待检验叙述;运行后须报告样本量、分母与误差,不直接发布拐点、最优或因果结论。
# 候选敏感性点包括0.002、0.07、0.2、0.55、0.57与0.85;每一点均需与事先设定基线比较。
# 若样本或期间不足,说明需要检查的数据和缺少的内容。运行后核对:核对 data、huigui01、huigui03、profit_8discount 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。
拓展练习:只改变一个关键输入或业务场景,先预测输出如何变化,再运行验证并解释变化原因。
Discount×Product Name×Order Date 单元数、单元均值、折扣组总额及利润守恒检查;不得使用“产品数×天数”作为稀疏订单分母。revenue/sell_exp/net_profit 到销售费用率和净利率的计算步骤;如果分母为零、报告版本冲突或期间超出 2018—2025,应先检查原因。Order Date/Sub-Category/Discount/Profit/Quantity/Sales/Product Name;中国代理资产为 stock_basic_data 与 financial_statement,字段 ts_code/name/area/end_date/revenue/sell_exp/net_profit,固定 2018—2025 年import pandas as pd # 导入表格库
def summarize_platform_discounts(superstore): # 定义平台折扣分析接口
required={'Order Date','Sub-Category','Discount','Profit','Quantity','Sales','Product Name'} # 对齐平台任务字段
if not required<=set(superstore.columns): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 缺列时停止
clean=superstore.dropna(subset=list(required)).drop_duplicates().copy() # 清除关键缺失与完全重复行
if clean.empty: raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 空样本时停止
clean['Order Date']=pd.to_datetime(clean['Order Date']) # 统一订单日期
cells=clean.groupby(['Discount','Product Name','Order Date'],as_index=False).agg(quantity=('Quantity','sum'),sales=('Sales','sum'),profit=('Profit','sum')) # 合并实际出现的折扣产品日单元
grouped=cells.groupby('Discount').agg(quantity=('quantity','sum'),sales=('sales','sum'),profit=('profit','sum'),observed_cells=('profit','size'),profit_per_product_day=('profit','mean'),sales_per_product_day=('sales','mean'),quantity_per_product_day=('quantity','mean')) # 在实际观测单元上汇总总额与均值
if grouped['observed_cells'].eq(0).any(): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 实际观测单元为零时停止
assert grouped['profit'].sum()==clean['Profit'].sum()==cells['profit'].sum() # 核对两层聚合利润守恒
return grouped,{'raw_rows':len(superstore),'clean_rows':len(clean),'profit_total':clean['Profit'].sum()} # 返回行数与利润核对结果def select_latest_disclosure(frame,value_fields): # 统一财报披露版本选择约定
required={'ts_code','end_date',*value_fields}; versions=[c for c in ['ann_date','f_ann_date'] if c in frame.columns] # 固定键与可接受版本字段
if not required<=set(frame.columns): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 缺列时停止
work=frame.dropna(subset=list(required)).copy(); work['end_date']=pd.to_datetime(work['end_date'],errors='coerce') # 清理必需字段
for column in versions: work[column]=pd.to_datetime(work[column],errors='coerce') # 解析披露日期
keys=['ts_code','end_date']; work=work.dropna(subset=['end_date']); duplicate_mask=work.duplicated(keys,keep=False); before=int(duplicate_mask.sum()) # 统计选择前重复
if before and (not versions or work.loc[duplicate_mask,versions].isna().all(axis=1).any()): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 无版本依据时停止
work['_version']=work[versions].max(axis=1) if versions else pd.NaT # 合并双版本字段为排序键
latest_version=work.groupby(keys)['_version'].transform('max') if versions else pd.Series(pd.NaT,index=work.index) # 为每个公司报告期定位最晚披露时点
latest_rows=work.loc[~duplicate_mask | work['_version'].eq(latest_version)].copy() # 仅保留单例或并列最晚版本
tied_mask=latest_rows.duplicated(keys,keep=False); tied_latest_rows=int(tied_mask.sum()) # 统计并列最晚版本行
conflicting_latest_keys=int(latest_rows.loc[tied_mask].groupby(keys,dropna=False)[value_fields].nunique(dropna=False).gt(1).any(axis=1).sum()) if tied_latest_rows else 0 # 统计并列且数值冲突的披露键
if conflicting_latest_keys: raise ValueError(f'输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 并列最新值冲突时请先检查数据后再继续
selected=latest_rows.drop_duplicates(keys+value_fields).drop_duplicates(keys).drop(columns='_version',errors='ignore') # 仅合并数值完全一致的并列行
after=int(selected.duplicated(keys).sum()); audit={'duplicate_rows_before':before,'tied_latest_rows':tied_latest_rows,'conflicting_latest_keys':conflicting_latest_keys,'duplicate_rows_after':after,'version_fields':versions} # 返回并列与冲突检查
if after: raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 选择后仍重复则停止
return selected,audit # 返回唯一版本与检查依据from pathlib import Path # 导入路径工具
import pandas as pd # 导入表格库
import numpy as np # 导入有限值检查工具
root=Path('/home/ubuntu/r2_data_mount/data/stock') # 指定股票目录
basic_path,fs_path=root/'stock_basic_data.h5',root/'financial_statement.h5' # 指定可用HDF资产
if not basic_path.exists() or not fs_path.exists(): raise FileNotFoundError('未找到课程数据文件,请从课程数据下载入口获取并核对文件位置') # 缺失即停止
basic=pd.read_hdf(basic_path,key='stock_basic_info',columns=['order_book_id','symbol','province']).rename(columns={'order_book_id':'raw_code','symbol':'name','province':'area'}) # 只读取公司筛选字段并标准化
basic['ts_code']=basic['raw_code'].str.replace('.XSHG','.SH',regex=False).str.replace('.XSHE','.SZ',regex=False) # 统一证券代码后缀
basic['area']=basic['area'].astype('string').str.replace(r'[省市]$','',regex=True) # 统一省市简称
required_basic={'ts_code','name','area'} # 定义公司对象字段
required_financial={'ts_code','end_date','revenue','sell_exp','net_profit'} # 定义销售费用率与净利率字段
focus=basic.loc[basic['ts_code'].eq('002315.SZ'),['ts_code','name','area']] # 锁定南京焦点科技
if len(focus)!=1: raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 对象不唯一时停止
focus_raw_code=basic.loc[basic['ts_code'].eq('002315.SZ'),'raw_code'].iloc[0] # 取得HDF原始代码以高效筛选
fs=pd.read_hdf(fs_path,key='financial_data',where='order_book_id == focus_raw_code',columns=['order_book_id','quarter','info_date','operating_revenue','selling_expense','net_profit']).rename(columns={'order_book_id':'ts_code','quarter':'end_date','info_date':'ann_date','operating_revenue':'revenue','selling_expense':'sell_exp'}) # 按目标公司读取营收、销售费用与净利润
fs['ts_code']=fs['ts_code'].str.replace('.XSHG','.SH',regex=False).str.replace('.XSHE','.SZ',regex=False) # 统一财报证券代码后缀
fs['end_date']=pd.PeriodIndex(fs['end_date'].str.upper(),freq='Q').end_time.normalize() # 将季度键转换为报告期末日期
if not required_basic<=set(basic.columns) or not required_financial<=set(fs.columns): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 标准化后仍缺列即停止
fs,version_audit=select_latest_disclosure(fs,['revenue','sell_exp','net_profit']) # 统一保留最晚披露版本
analysis=fs.merge(focus,on='ts_code',how='inner',validate='many_to_one').sort_values('end_date').copy() # 连接财报与公司信息并固定时间顺序
analysis=analysis.loc[analysis['end_date'].between('2018-01-01','2025-12-31')].copy() # 严格锁定2018—2025报告期if analysis.empty or analysis['end_date'].nunique()<5: raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 空连接或期间不足时停止
if analysis['revenue'].eq(0).any() or not np.isfinite(analysis[['revenue','sell_exp','net_profit']].to_numpy()).all(): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 比率检查要求
analysis['selling_expense_rate']=analysis['sell_exp']/analysis['revenue'] # 计算销售费用率
analysis['net_profit_margin']=analysis['net_profit']/analysis['revenue'] # 计算净利率并与任务口径一致
if not np.isfinite(analysis[['selling_expense_rate','net_profit_margin']].to_numpy()).all(): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 非有限比率停止
analysis['fiscal_year']=analysis['end_date'].dt.year # 构造财政年键
analysis['fiscal_quarter']=analysis['end_date'].dt.quarter # 构造财政季度键
if analysis.duplicated(['fiscal_year','fiscal_quarter']).any(): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 披露选择后期间键仍不唯一时停止
prior_revenue=analysis[['fiscal_year','fiscal_quarter','revenue']].rename(columns={'revenue':'prior_year_revenue'}).copy() # 建立上年同季收入查找表
prior_revenue['fiscal_year']=prior_revenue['fiscal_year']+1 # 将历史年键前移一年以匹配当期
analysis=analysis.merge(prior_revenue,on=['fiscal_year','fiscal_quarter'],how='left',validate='one_to_one') # 按年与季度精确连接同比分母
analysis['revenue_yoy']=np.where(analysis['prior_year_revenue'].notna() & analysis['prior_year_revenue'].ne(0),analysis['revenue']/analysis['prior_year_revenue']-1,np.nan) # 仅在上年同季分母可用时计算同比
print(version_audit,analysis[['ts_code','name','area','end_date','revenue','selling_expense_rate','net_profit_margin','revenue_yoy']]) # 输出数据版本检查与双指标表ratio_check=analysis[['sell_exp','net_profit']].div(analysis['revenue'],axis=0) # 独立重算销售费用率与净利率
assert ratio_check['sell_exp'].equals(analysis['selling_expense_rate']) # 核对销售费用率
assert ratio_check['net_profit'].equals(analysis['net_profit_margin']) # 核对净利率
sensitivity=pd.DataFrame({'selling_rate_ceiling':[.03,.05,.08],'net_margin_floor':[.05,.03,0]}) # 定义三组双最低要求课堂情景
sensitivity['latest_selling_rate']=analysis['selling_expense_rate'].iloc[-1] # 固定最新报告期费用率
sensitivity['latest_net_margin']=analysis['net_profit_margin'].iloc[-1] # 固定最新报告期净利率
sensitivity['status']=sensitivity.apply(lambda row:'REVIEW' if row['latest_selling_rate']>row['selling_rate_ceiling'] or row['latest_net_margin']<row['net_margin_floor'] else 'PASS',axis=1) # 任一最低要求失败即核对
sensitivity['threshold_status']='课堂预算情景,待真实预算批准' # 标明阈值尚非生产政策
sensitivity['owner']='跨境电商财务负责人' # 指定核对责任角色
sensitivity['action']=sensitivity['status'].map({'REVIEW':'暂缓扩大使用并核对费用','PASS':'继续观察'}) # 记录建议
sensitivity['recheck_when']='输入字段改变或连续两期超预算' # 定义升级需要重新检查的情况
sensitivity['recheck_date']='下一季度财报公布后第5个工作日' # 事先设定再次检查时间
assert analysis['end_date'].between('2018-01-01','2025-12-31').all() # 独立核验固定期间
print({'n':len(analysis),'start':analysis['end_date'].min(),'end':analysis['end_date'].max()},sensitivity) # 输出期间与双最低要求alternative_raw_code='300792.XSHE' # 事先设定同属互联网服务业的浙江壹网壹创
alternative_financials=pd.read_hdf(fs_path,key='financial_data',where='order_book_id == alternative_raw_code',columns=['order_book_id','quarter','info_date','operating_revenue','selling_expense','net_profit']).rename(columns={'order_book_id':'ts_code','quarter':'end_date','info_date':'ann_date','operating_revenue':'revenue','selling_expense':'sell_exp'}) # 按相同真实字段结构读取新案例公司财报
alternative_financials['ts_code']=alternative_financials['ts_code'].str.replace('.XSHE','.SZ',regex=False) # 统一新案例公司证券代码后缀
alternative_financials['end_date']=pd.PeriodIndex(alternative_financials['end_date'].str.upper(),freq='Q').end_time.normalize() # 将新案例公司季度键转为报告期末
alternative_financials,alternative_version_audit=select_latest_disclosure(alternative_financials,['revenue','sell_exp','net_profit']) # 按同一规则选择最晚披露版本
alternative_analysis=alternative_financials.loc[alternative_financials['end_date'].between('2018-01-01','2025-12-31')].sort_values('end_date').copy() # 严格锁定新案例公司2018—2025期间
if alternative_analysis.empty or alternative_analysis['revenue'].eq(0).any(): raise ValueError('输入文件、字段、样本量或数值不符合当前分析要求,请按本页说明检查') # 没有有效分母时停止
alternative_analysis['selling_expense_rate']=alternative_analysis['sell_exp']/alternative_analysis['revenue'] # 按相同口径计算新案例销售费用率
alternative_analysis['net_profit_margin']=alternative_analysis['net_profit']/alternative_analysis['revenue'] # 按相同口径计算新案例净利率
alternative_latest=alternative_analysis.iloc[-1] # 取得新案例公司最新合规报告期
alternative_status='REVIEW' if alternative_latest['selling_expense_rate']>.05 or alternative_latest['net_profit_margin']<.03 else 'PASS' # 使用同一基准双最低要求形成新案例状态
assert alternative_analysis['end_date'].between('2018-01-01','2025-12-31').all() # 核验新案例样本仍在固定期间
print({'source_rows':len(analysis),'alternative_rows':len(alternative_analysis),'source_latest':analysis[['selling_expense_rate','net_profit_margin']].iloc[-1].to_dict(),'alternative_latest':alternative_latest[['selling_expense_rate','net_profit_margin']].to_dict(),'alternative_status':alternative_status},alternative_version_audit) # 输出新案例前后结构、比率与数据版本检查.05/.03 基准最低要求;输出拓展应用前后行数、最新费用率/净利率和状态,并用期间检查及数据版本检查核对。sell_exp 却声称计算费用率;用名称连接;累计和单季口径混用[商业大数据分析与应用]